package com.shuai.clientsdk.client;


import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.shuai.clientsdk.Const.URLEnum;
import com.shuai.clientsdk.common.BaseResponse;
import com.shuai.clientsdk.constant.AuthenticationConstant;
import com.shuai.clientsdk.exc.OpenAPIException;
import com.shuai.clientsdk.model.file.FileResponse;
import com.shuai.clientsdk.utils.AuthenticationSignUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class BaixsAPIClient {

    private String apiKey;
    private String apiSecret;

    private final RestTemplate restTemplate = new RestTemplate();

    private final String serverURL = "http://127.0.0.1:8082/";
    private final String gatewayServer = "http://127.0.0.1:8086";

    public BaixsAPIClient(String apiKey, String apiSecret) {
        this.apiKey = apiKey;
        this.apiSecret = apiSecret;
    }


    private MultiValueMap<String, String> getReqHeaders(Map<String, String> paramsMap, boolean setContentType) {
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        if (setContentType) {
            headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
        }
        headers.add(AuthenticationConstant.API_KEY, apiKey);
        headers.add(AuthenticationConstant.API_SECRET, apiSecret);
        int random = RandomUtil.randomInt(1, 9999);
        headers.add(AuthenticationConstant.RANDOM, Integer.toString(random));
        long timestamp = System.currentTimeMillis();
        headers.add(AuthenticationConstant.TIMESTAMP, Long.toString(timestamp));
        String sign = AuthenticationSignUtils.generateSign(apiKey, apiSecret, timestamp, JSONUtil.toJsonStr(paramsMap));
        headers.add(AuthenticationConstant.SIGN, sign);
        return headers;
    }

    private MultiValueMap<String, String> getFileReqHeaders(boolean setContentType) {
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        if (setContentType) {
            headers.add("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE);
        }
        headers.add(AuthenticationConstant.API_KEY, apiKey);
        headers.add(AuthenticationConstant.API_SECRET, apiSecret);
        int random = RandomUtil.randomInt(1, 9999);
        headers.add(AuthenticationConstant.RANDOM, Integer.toString(random));
        long timestamp = System.currentTimeMillis();
        headers.add(AuthenticationConstant.TIMESTAMP, Long.toString(timestamp));
        String sign = AuthenticationSignUtils.generateSign(apiKey, apiSecret, timestamp, "file");
        headers.add(AuthenticationConstant.SIGN, sign);
        return headers;
    }


    /**
     * @param file MultipartFile文件对象
     * @return 返回保存成功的文件名
     */
    public FileResponse uploadFile(MultipartFile file) throws URISyntaxException, IOException, OpenAPIException {
        byte[] fileData = file.getBytes();
        //请求头
        MultiValueMap<String, String> headers = getFileReqHeaders(true);

        //请求传参
        ByteArrayResource fileAsResource = new ByteArrayResource(fileData) {
            @Override
            public String getFilename() {
                return file.getOriginalFilename();
            }

            @Override
            public long contentLength() {
                return fileData.length;
            }
        };
        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        param.add("file", fileAsResource);

        //构建uri
        URI uri = new URI(serverURL + URLEnum.FILE_UPLOAD_URL);

        //传参
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(param, headers);
        //调用接口
        ResponseEntity<BaseResponse<Object>> responseEntity = restTemplate.exchange(
                uri,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<BaseResponse<Object>>() {
                });
        BaseResponse<Object> responseBody = responseEntity.getBody();
        if (responseBody == null) {
            throw new OpenAPIException("上传文件失败，OPENAPI平台服务异常");
        }
        if (responseBody.getCode() != 0) {
            if (responseBody.getCode() == 40101) {
                throw new OpenAPIException("文件上传失败，无权限");
            }
            throw new OpenAPIException("文件上传失败 openAPI-Failed!");
        }
        // 正确响应
        FileResponse fileResponse = JSONUtil.toBean(JSONUtil.toJsonStr(responseBody.getData()), FileResponse.class);
        return fileResponse;
    }


    public void downloadFile(String fileStorageName, HttpServletResponse response) throws OpenAPIException, IOException {
        HashMap<String, String> fileReqParam = new HashMap<>();
        if (StringUtils.isBlank(fileStorageName)) {
            throw new OpenAPIException("下载的文件名不能为空");
        }
        fileReqParam.put("storageName", fileStorageName);
        ResponseExtractor<Boolean> responseExtractor = clientHttpResponse -> {
            // 设置响应头，直接用第三方文件服务的响应头
            HttpHeaders headers = clientHttpResponse.getHeaders();
            headers.forEach((key, value) -> response.setHeader(key, value.get(0)));
            // 收到响应输入流即时拷贝写出到响应输出流中: inputStream -> outputStream
            StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
            return true;
        };
        RequestCallback requestCallback = request -> {
            request.getHeaders().addAll(this.getReqHeaders(fileReqParam, true));
            request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
            request.getBody().write(JSONUtil.toJsonStr(fileReqParam).getBytes());
        };
        String requestURL = serverURL + URLEnum.FILE_DOWNLOAD_URL;
        restTemplate.execute(requestURL, HttpMethod.POST, requestCallback, responseExtractor);
    }


    /**
     * 在线测试接口
     */
    public Object onlineInvokeTest(String apiUrl, String paramsStr) throws OpenAPIException {
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add(AuthenticationConstant.API_KEY, apiKey);
        headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
        headers.add(AuthenticationConstant.API_SECRET, apiSecret);
        int random = RandomUtil.randomInt(1, 9999);
        headers.add(AuthenticationConstant.RANDOM, Integer.toString(random));
        long timestamp = System.currentTimeMillis();
        headers.add(AuthenticationConstant.TIMESTAMP, Long.toString(timestamp));
        String sign = AuthenticationSignUtils.generateSign(apiKey, apiSecret, timestamp, null);
        headers.add(AuthenticationConstant.SIGN, sign);
        HttpEntity<String> httpEntity = new HttpEntity<>(paramsStr, headers);
        BaseResponse baseResponse = restTemplate.postForObject(apiUrl, httpEntity, BaseResponse.class);
        return JSONUtil.toJsonStr(baseResponse);
    }

    public Object getWeather(String apiUrl) {
        return null;
    }
}
